home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / programr / ole2book.zip / CHAP06.ZIP / CHAP06 / FREELOAD / DOCUMENT.CPP < prev    next >
C/C++ Source or Header  |  1993-05-21  |  17KB  |  658 lines

  1. /*
  2.  * DOCUMENT.CPP
  3.  *
  4.  * Implementation of the CFreeloaderDoc derivation of CDocument.
  5.  * We create a default handler object and use it for drawing, data
  6.  * caching, and serialization.
  7.  *
  8.  * Copyright (c)1993 Microsoft Corporation, All Rights Reserved
  9.  *
  10.  * Kraig Brockschmidt, Software Design Engineer
  11.  * Microsoft Systems Developer Relations
  12.  *
  13.  * Internet  :  kraigb@microsoft.com
  14.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  15.  */
  16.  
  17.  
  18. #include "freeload.h"
  19.  
  20.  
  21.  
  22. /*
  23.  * CFreeloaderDoc::CFreeloaderDoc
  24.  * CFreeloaderDoc::~CFreeloaderDoc
  25.  *
  26.  * Constructor Parameters:
  27.  *  hInst           HINSTANCE of the application.
  28.  */
  29.  
  30. CFreeloaderDoc::CFreeloaderDoc(HINSTANCE hInst)
  31.     : CDocument(hInst)
  32.     {
  33.     m_pIStorage=NULL;
  34.     m_pIUnknown=NULL;
  35.     m_dwConn=0;
  36.     return;
  37.     }
  38.  
  39.  
  40. CFreeloaderDoc::~CFreeloaderDoc(void)
  41.     {
  42.     ReleaseObject();
  43.  
  44.     if (NULL!=m_pIStorage)
  45.         m_pIStorage->Release();
  46.  
  47.     return;
  48.     }
  49.  
  50.  
  51.  
  52.  
  53. /*
  54.  * CFreeloaderDoc::ReleaseObject
  55.  *
  56.  * Purpose:
  57.  *  Centralizes cleanup code for the object and its cache.
  58.  *
  59.  * Parameters:
  60.  *  None
  61.  *
  62.  * Return Value:
  63.  *  None
  64.  */
  65.  
  66. void CFreeloaderDoc::ReleaseObject(void)
  67.     {
  68.     LPOLECACHE      pIOleCache;
  69.     HRESULT         hr;
  70.  
  71.     if (0!=m_dwConn)
  72.         {
  73.         hr=m_pIUnknown->QueryInterface(IID_IOleCache
  74.             , (LPVOID FAR *)&pIOleCache);
  75.  
  76.         if (SUCCEEDED(hr))
  77.             {
  78.             pIOleCache->Uncache(m_dwConn);
  79.             pIOleCache->Release();
  80.             }
  81.         }
  82.  
  83.     if (NULL!=m_pIUnknown)
  84.         m_pIUnknown->Release();
  85.  
  86.     CoFreeUnusedLibraries();
  87.  
  88.     m_dwConn=0;
  89.     m_pIUnknown=NULL;
  90.     return;
  91.     }
  92.  
  93.  
  94.  
  95.  
  96.  
  97. /*
  98.  * CFreeloaderDoc::FMessageHook
  99.  *
  100.  * Purpose:
  101.  *  Processes WM_PAINT for the document so we can draw the object.
  102.  *
  103.  * Parameters:
  104.  *  <WndProc Parameters>
  105.  *  pLRes           LRESULT FAR * in which to store the return value
  106.  *                  for the message.
  107.  *
  108.  * Return Value:
  109.  *  BOOL            TRUE to prevent further processing, FALSE otherwise.
  110.  */
  111.  
  112. BOOL CFreeloaderDoc::FMessageHook(HWND hWnd, UINT iMsg, WPARAM wParam
  113.     , LPARAM lParam, LRESULT FAR *pLRes)
  114.     {
  115.     PAINTSTRUCT     ps;
  116.     HDC             hDC;
  117.     RECT            rc;
  118.     RECTL           rcl;
  119.     LPVIEWOBJECT    pIViewObject;
  120.     HRESULT         hr;
  121.  
  122.     if (WM_PAINT!=iMsg)
  123.         return FALSE;
  124.  
  125.     hDC=BeginPaint(hWnd, &ps);
  126.     GetClientRect(hWnd, &rc);
  127.  
  128.     /*
  129.      * To draw the object we can either QueryInterface for an IViewObject,
  130.      * call IViewObject::Draw, and IViewObject::Release, or we can use
  131.      * OleDraw which does exactly the same three steps, only calling
  132.      * ::Draw with defaults.  OleDraw does exactly what is done here.
  133.      */
  134.  
  135.     if (NULL!=m_pIUnknown)
  136.         {
  137.         hr=m_pIUnknown->QueryInterface(IID_IViewObject
  138.             , (LPVOID FAR *)&pIViewObject);
  139.  
  140.         if (SUCCEEDED(hr))
  141.             {
  142.            #ifndef WIN32
  143.             rcl.left  =MAKELONG(rc.left, 0);
  144.             rcl.right =MAKELONG(rc.right, 0);
  145.             rcl.top   =MAKELONG(rc.top, 0);
  146.             rcl.bottom=MAKELONG(rc.bottom, 0);
  147.            #else
  148.             //Win32 rectangles are already LONGs.
  149.             rcl=rc;
  150.            #endif
  151.  
  152.             pIViewObject->Draw(DVASPECT_CONTENT, -1, NULL, NULL, 0, hDC
  153.                 , &rcl, NULL, NULL, 0);
  154.             pIViewObject->Release();
  155.             }
  156.         }
  157.  
  158.     EndPaint(hWnd, &ps);
  159.  
  160.     return FALSE;
  161.     }
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169. /*
  170.  * CFreeloaderDoc::ULoad
  171.  *
  172.  * Purpose:
  173.  *  Loads a given document without any user interface overwriting the
  174.  *  previous contents of the Polyline window.  We do this by opening
  175.  *  the file and telling the Polyline to load itself from that file.
  176.  *
  177.  * Parameters:
  178.  *  fChangeFile     BOOL indicating if we're to update the window title
  179.  *                  and the filename from using this file.
  180.  *  pszFile         LPSTR to the filename to load, NULL if the file is
  181.  *                  new and untitled.
  182.  *
  183.  * Return Value:
  184.  *  UINT            An error value from DOCERR_*
  185.  */
  186.  
  187. UINT CFreeloaderDoc::ULoad(BOOL fChangeFile, LPSTR pszFile)
  188.     {
  189.     HRESULT             hr;
  190.     CLSID               clsID;
  191.     LPSTORAGE           pIStorage;
  192.     LPUNKNOWN           pIUnknown;
  193.     LPPERSISTSTORAGE    pIPersistStorage;
  194.     DWORD               dwMode=STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
  195.  
  196.     if (NULL==pszFile)
  197.         {
  198.         //Create a new temp file.
  199.         hr=StgCreateDocfile(NULL, dwMode | STGM_CREATE | STGM_DELETEONRELEASE
  200.             , 0, &pIStorage);
  201.  
  202.         if (FAILED(hr))
  203.             return DOCERR_COULDNOTOPEN;
  204.  
  205.         m_pIStorage=pIStorage;
  206.  
  207.         FDirtySet(FALSE);
  208.         Rename(NULL);
  209.         return DOCERR_NONE;
  210.         }
  211.  
  212.     //Attempt to open the storage.
  213.     hr=StgOpenStorage(pszFile, NULL, dwMode, NULL, 0, &pIStorage);
  214.  
  215.     if (FAILED(hr))
  216.         return DOCERR_COULDNOTOPEN;
  217.  
  218.     /*
  219.      * When we previously called IPersistStorage::Save, OLE2.DLL kindly
  220.      * placed a CLSID into the IStorage for us, either CLSID_StaticMetafile,
  221.      * known to us as CLSID_FreeMetafile, or CLSID_StaticDib, known to us
  222.      * as CLSID_FreeDib.  All we have to do is load this CLSID,
  223.      * create the object for that ID (using CoCreateInstance, see ::FPaste
  224.      * below).
  225.      */
  226.  
  227.     hr=ReadClassStg(pIStorage, &clsID);
  228.  
  229.     //See if we know about it.
  230.     if (FAILED(hr) || !(IsEqualCLSID(clsID, CLSID_FreeMetafile)
  231.         || IsEqualCLSID(clsID, CLSID_FreeDib)))
  232.         {
  233.         pIStorage->Release();
  234.         return DOCERR_READFAILURE;
  235.         }
  236.  
  237.     //Go create an object, then tell *it* to load the data.
  238.     hr=CoCreateInstance(clsID, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown
  239.         , (LPVOID FAR *)&pIUnknown);
  240.  
  241.     if (FAILED(hr))
  242.         {
  243.         pIStorage->Release();
  244.         return DOCERR_READFAILURE;
  245.         }
  246.  
  247.     //Get IPersistStorage for the data we hold.
  248.     pIUnknown->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPersistStorage);
  249.  
  250.     hr=pIPersistStorage->Load(pIStorage);
  251.     pIPersistStorage->Release();
  252.  
  253.     if (FAILED(hr))
  254.         {
  255.         pIUnknown->Release();
  256.         pIStorage->Release();
  257.         return DOCERR_READFAILURE;
  258.         }
  259.  
  260.     m_pIStorage=pIStorage;
  261.     m_pIUnknown=pIUnknown;
  262.  
  263.     Rename(pszFile);
  264.     FDirtySet(FALSE);
  265.     return DOCERR_NONE;
  266.     }
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274. /*
  275.  * CFreeloaderDoc::USave
  276.  *
  277.  * Purpose:
  278.  *  Writes the file to a known filename, requiring that the user has
  279.  *  previously used FileOpen or FileSaveAs in order to have a filename.
  280.  *
  281.  * Parameters:
  282.  *  uType           UINT indicating the type of file the user requested
  283.  *                  to save in the File Save As dialog.
  284.  *  pszFile         LPSTR under which to save.  If NULL, use the current name.
  285.  *
  286.  * Return Value:
  287.  *  UINT            An error value from DOCERR_*
  288.  */
  289.  
  290. UINT CFreeloaderDoc::USave(UINT uType, LPSTR pszFile)
  291.     {
  292.     HRESULT             hr;
  293.     LPSTORAGE           pIStorage;
  294.     LPPERSISTSTORAGE    pIPersistStorage;
  295.     CLSID               clsID;
  296.  
  297.     //If we have no data object, there's nothing to save.
  298.     if (NULL==m_pIUnknown)
  299.         return DOCERR_WRITEFAILURE;
  300.  
  301.     //Get IPersistStorage for the data we hold.
  302.     hr=m_pIUnknown->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPersistStorage);
  303.  
  304.     if (FAILED(hr))
  305.         return DOCERR_WRITEFAILURE;
  306.  
  307.     //Save or Save As with the same file is just a commit.
  308.     if (NULL==pszFile || (NULL!=pszFile && 0==lstrcmpi(pszFile, m_szFile)))
  309.         {
  310.         pIPersistStorage->Save(m_pIStorage, TRUE);
  311.         m_pIStorage->Commit(STGC_ONLYIFCURRENT);
  312.  
  313.         pIPersistStorage->SaveCompleted(m_pIStorage);
  314.         pIPersistStorage->Release();
  315.  
  316.         FDirtySet(FALSE);
  317.         return DOCERR_NONE;
  318.         }
  319.  
  320.     /*
  321.      * When we're given a name, open the storage, creating it new if
  322.      * it does not exist or overwriting the old one.  Then ::CopyTo
  323.      * from the current to the new, ::Commit the new, then ::Release
  324.      * the old.
  325.      */
  326.  
  327.     hr=StgCreateDocfile(pszFile, STGM_TRANSACTED | STGM_READWRITE
  328.         | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, &pIStorage);
  329.  
  330.     if (FAILED(hr))
  331.         return DOCERR_COULDNOTOPEN;
  332.  
  333.     //Insure the image is up to date, then tell it we're changing
  334.     pIPersistStorage->Save(m_pIStorage, TRUE);
  335.     pIPersistStorage->HandsOffStorage();
  336.  
  337.     //Save the class, bitmap or metafile
  338.     pIPersistStorage->GetClassID(&clsID);
  339.     hr=WriteClassStg(m_pIStorage, clsID);
  340.  
  341.     hr=m_pIStorage->CopyTo(NULL, NULL, NULL, pIStorage);
  342.  
  343.     if (FAILED(hr))
  344.         {
  345.         pIPersistStorage->SaveCompleted(m_pIStorage);
  346.         pIPersistStorage->Release();
  347.         pIStorage->Release();
  348.         return DOCERR_WRITEFAILURE;
  349.         }
  350.  
  351.     pIStorage->Commit(STGC_ONLYIFCURRENT);
  352.  
  353.     /*
  354.      * Revert changes on the original storage.  If this was a temp file,
  355.      * it's deleted since we used STGM_DELETEONRELEASE.
  356.      */
  357.     m_pIStorage->Release();
  358.  
  359.     //Make this new storage current
  360.     m_pIStorage=pIStorage;
  361.     pIPersistStorage->SaveCompleted(m_pIStorage);
  362.     pIPersistStorage->Release();
  363.  
  364.     Rename(pszFile);
  365.     FDirtySet(FALSE);
  366.     return DOCERR_NONE;
  367.     }
  368.  
  369.  
  370.  
  371.  
  372.  
  373.  
  374. /*
  375.  * CFreeloaderDoc::FClip
  376.  *
  377.  * Purpose:
  378.  *  Places a private format, a metafile, and a bitmap of the display
  379.  *  on the clipboard, optionally implementing Cut by deleting the
  380.  *  data in the current window after rendering.
  381.  *
  382.  * Parameters:
  383.  *  hWndFrame       HWND of the main window.
  384.  *  fCut            BOOL indicating cut (TRUE) or copy (FALSE).
  385.  *
  386.  * Return Value:
  387.  *  BOOL            TRUE if successful, FALSE otherwise.
  388.  */
  389.  
  390. BOOL CFreeloaderDoc::FClip(HWND hWndFrame, BOOL fCut)
  391.     {
  392.     BOOL            fRet=TRUE;
  393.     static UINT     rgcf[3]={CF_METAFILEPICT, CF_DIB, CF_BITMAP};
  394.     const UINT      cFormats=3;
  395.     UINT            i;
  396.     HGLOBAL         hMem;
  397.  
  398.     if (NULL==m_pIUnknown)
  399.         return FALSE;
  400.  
  401.     if (!OpenClipboard(hWndFrame))
  402.         return FALSE;
  403.  
  404.     //Clean out whatever junk is in the clipboard.
  405.     EmptyClipboard();
  406.  
  407.     for (i=0; i < cFormats; i++)
  408.         {
  409.         hMem=RenderFormat(rgcf[i]);
  410.  
  411.         if (NULL!=hMem)
  412.             {
  413.             SetClipboardData(rgcf[i], hMem);
  414.             fRet=TRUE;
  415.             break;
  416.             }
  417.         }
  418.  
  419.     //Free clipboard ownership.
  420.     CloseClipboard();
  421.  
  422.     //If we're cutting, clean out the cache and the object we hold.
  423.     if (fRet && fCut)
  424.         {
  425.         ReleaseObject();
  426.         InvalidateRect(m_hWnd, NULL, TRUE);
  427.         UpdateWindow(m_hWnd);
  428.         FDirtySet(TRUE);
  429.         }
  430.  
  431.     return fRet;
  432.     }
  433.  
  434.  
  435.  
  436.  
  437.  
  438. /*
  439.  * CFreeloaderDoc::RenderFormat
  440.  *
  441.  * Purpose:
  442.  *  Renders a specific clipboard format into global memory.  We have this
  443.  *  function split out because we'll eventually move to delayed rendering
  444.  *  and this will then be immediately callable from the frame.
  445.  *
  446.  * Parameters:
  447.  *  cf              UINT format to render.
  448.  *
  449.  * Return Value:
  450.  *  HGLOBAL         Global memory handle containing the data.
  451.  */
  452.  
  453. HGLOBAL CFreeloaderDoc::RenderFormat(UINT cf)
  454.     {
  455.     LPDATAOBJECT        pIDataObject;
  456.     FORMATETC           fe;
  457.     STGMEDIUM           stm;
  458.  
  459.     if (NULL==m_pIUnknown)
  460.         return NULL;
  461.  
  462.     //We only have to ask the data object (cache) for the data.
  463.     switch (cf)
  464.         {
  465.         case CF_METAFILEPICT:
  466.             stm.tymed=TYMED_MFPICT;
  467.             break;
  468.  
  469.        case CF_DIB:
  470.             stm.tymed=TYMED_HGLOBAL;
  471.             break;
  472.  
  473.        case CF_BITMAP:
  474.             stm.tymed=TYMED_GDI;
  475.             break;
  476.  
  477.         default:
  478.             return NULL;
  479.         }
  480.  
  481.     stm.hGlobal=NULL;
  482.     SETFormatEtc(fe, cf, DVASPECT_CONTENT, NULL, stm.tymed, -1);
  483.  
  484.     m_pIUnknown->QueryInterface(IID_IDataObject, (LPVOID FAR *)&pIDataObject);
  485.     pIDataObject->GetData(&fe, &stm);
  486.     pIDataObject->Release();
  487.  
  488.     return stm.hGlobal;
  489.     }
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496. /*
  497.  * CFreeloaderDoc::FQueryPaste
  498.  *
  499.  * Purpose:
  500.  *  Determines if we can paste data from the clipboard.
  501.  *
  502.  * Parameters:
  503.  *  None
  504.  *
  505.  * Return Value:
  506.  *  BOOL            TRUE if data is available, FALSE otherwise.
  507.  */
  508.  
  509. BOOL CFreeloaderDoc::FQueryPaste(void)
  510.     {
  511.     return IsClipboardFormatAvailable(CF_BITMAP)
  512.         || IsClipboardFormatAvailable(CF_DIB)
  513.         || IsClipboardFormatAvailable(CF_METAFILEPICT);
  514.     }
  515.  
  516.  
  517.  
  518.  
  519.  
  520. /*
  521.  * CFreeloaderDoc::FPaste
  522.  *
  523.  * Purpose:
  524.  *  Retrieves the private data format from the clipboard and sets it
  525.  *  to the current figure in the editor window.
  526.  *
  527.  *  Note that if this function is called, then the clipboard format
  528.  *  is available because the Paste menu item is only enabled if the
  529.  *  format is present.
  530.  *
  531.  * Parameters:
  532.  *  hWndFrame       HWND of the main window.
  533.  *
  534.  * Return Value:
  535.  *  BOOL            TRUE if successful, FALSE otherwise.
  536.  */
  537.  
  538. BOOL CFreeloaderDoc::FPaste(HWND hWndFrame)
  539.     {
  540.     UINT                cf=0;
  541.     BOOL                fRet=FALSE;
  542.     HRESULT             hr;
  543.     DWORD               dwConn;
  544.     LPUNKNOWN           pIUnknown;
  545.     LPOLECACHE          pIOleCache;
  546.     LPPERSISTSTORAGE    pIPersistStorage;
  547.     FORMATETC           fe;
  548.     STGMEDIUM           stm;
  549.     CLSID               clsID;
  550.  
  551.     if (!OpenClipboard(hWndFrame))
  552.         return FALSE;
  553.  
  554.     /*
  555.      * Try to get data in order of metafile, dib, bitmap.  We set stm.tymed
  556.      * up front so if we actually get something a call to ReleaseStgMedium
  557.      * will clean it up for us.
  558.      */
  559.  
  560.     stm.tymed=TYMED_MFPICT;
  561.     stm.hGlobal=GetClipboardData(CF_METAFILEPICT);
  562.  
  563.     if (NULL!=stm.hGlobal)
  564.         cf=CF_METAFILEPICT;
  565.  
  566.     if (0==cf)
  567.         {
  568.         stm.tymed=TYMED_HGLOBAL;
  569.         stm.hGlobal=GetClipboardData(CF_DIB);
  570.  
  571.         if (NULL!=stm.hGlobal)
  572.             cf=CF_DIB;
  573.         }
  574.  
  575.     if (0==cf)
  576.         {
  577.         stm.tymed=TYMED_GDI;
  578.         stm.hGlobal=GetClipboardData(CF_BITMAP);
  579.  
  580.         if (NULL!=stm.hGlobal)
  581.             cf=CF_BITMAP;
  582.         }
  583.  
  584.     CloseClipboard();
  585.  
  586.     //Didn't get anything?  Then we're finished.
  587.     if (0==cf)
  588.         return FALSE;
  589.  
  590.     //This now describes the data we have.
  591.     SETFormatEtc(fe, cf, DVASPECT_CONTENT, NULL, stm.tymed, -1);
  592.  
  593.  
  594.     /*
  595.      * Create an object to deal with this data.  There's two ways to
  596.      * do this:  CoCreateInstance (CoGetClassObject) or
  597.      * OleCreateDefaultHandler.  The first will go through all the exercises
  598.      * of looking up the CLSID in the regDB, finding OLE2.DLL (registered
  599.      * for these classes), getting a class factory, and using
  600.      * IClassFactory::CreateInstance.
  601.      *
  602.      * The second method directly into ole2.dll which creates an object
  603.      * identical to that created with CoCreateInstnace.  This call is more
  604.      * terse and generally faster.
  605.      *
  606.      * This function always uses OleCreateDefaultHandler.  For an
  607.      * example of CoCreateInstance, see ::ULoad above.
  608.      */
  609.  
  610.     if (CF_METAFILEPICT==cf)
  611.         clsID=CLSID_FreeMetafile;
  612.     else
  613.         clsID=CLSID_FreeDib;
  614.  
  615.     hr=OleCreateDefaultHandler(clsID, NULL, IID_IUnknown, (LPVOID FAR *)&pIUnknown);
  616.  
  617.     if (FAILED(hr))
  618.         {
  619.         ReleaseStgMedium(&stm);
  620.         return FALSE;
  621.         }
  622.  
  623.     /*
  624.      * Our contract says we provide storage through IPersistStorage::InitNew.
  625.      * We know that the object we're dealing with supports IPersistStorage
  626.      * and IOleCache, so we don't bother to check return values.
  627.      * HACK:  Living Dangerously?
  628.      */
  629.     pIUnknown->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPersistStorage);
  630.     pIPersistStorage->InitNew(m_pIStorage);
  631.     pIPersistStorage->Release();
  632.  
  633.     //Now that we have the cache object, shove the data into it.
  634.     pIUnknown->QueryInterface(IID_IOleCache, (LPVOID FAR *)&pIOleCache);
  635.     pIOleCache->Cache(&fe, ADVF_PRIMEFIRST, &dwConn);
  636.  
  637.     hr=pIOleCache->SetData(&fe, &stm, TRUE);
  638.     pIOleCache->Release();
  639.  
  640.     if (FAILED(hr))
  641.         {
  642.         ReleaseStgMedium(&stm);
  643.         pIUnknown->Release();
  644.         return FALSE;
  645.         }
  646.  
  647.     //Now that that's all done, replace our current with the new.
  648.     ReleaseObject();
  649.     m_pIUnknown=pIUnknown;
  650.     m_dwConn=dwConn;
  651.  
  652.     FDirtySet(TRUE);
  653.  
  654.     InvalidateRect(m_hWnd, NULL, TRUE);
  655.     UpdateWindow(m_hWnd);
  656.     return TRUE;
  657.     }
  658.